﻿using Pvf.Core.Tests;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace Pvf.Core.Converters
{
    internal sealed class PvfObjectConverter<TObj> : PvfBinaryConverter<TObj>
    {
        //private List<PvfObjectLayoutWithWrapper> _propertyLayouts;

        private Dictionary<int, WrapperPvfObjectLayout> _propertyLayouts;

        protected override void OnInit()
        {
            if (Layout.Env == null)
            {
                throw new ArgumentNullException(nameof(Layout.Env));
            }

            Layout.IsRequired = Layout.ObjType.GetCustomAttribute<PvfRequiredAttribute>() != null;
            
            var propLst = Layout.ObjType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
             .Select(x =>
             {
                 var ignoreAttr = x.GetCustomAttribute<PvfIngoreAttribute>();
                 if (ignoreAttr != null)
                 {
                     return null;
                 }

                 var propertyLayout = new WrapperPvfObjectLayout(x.PropertyType, Layout.Env)
                 {
                     BaseProperty = x,
                     IsRequired = x.GetCustomAttribute<PvfRequiredAttribute>() != null,
                     IsEmpty = x.GetCustomAttribute<PvfEmptyAttribute>() != null,
                     IsDynamic = x.GetCustomAttribute<PvfDynamicElementAttribute>() != null
                 };

                 if (!Layout.IsRequired && !propertyLayout.IsDynamic && !propertyLayout.IsRequired)
                 {
                     var fieldAttr = x.GetCustomAttribute<PvfFieldsAttribute>();

                     var closedFieldAttr = x.GetCustomAttribute<PvfClosedAttribute>();

                     var names = new HashSet<string>();
                     var exFieldNamesAttr = x.GetCustomAttribute<PvfFieldsAttribute>();
                     if (exFieldNamesAttr != null)
                     {
                         foreach (var field in exFieldNamesAttr.Fields)
                         {
                             names.Add(field);
                         }
                     }
                     if (exFieldNamesAttr == null || exFieldNamesAttr.IncludeSourceField)
                     {
                         names.Add($"[{PvfStatics.NameCast(x.Name)}]");
                     }

                     foreach (var name in names)
                     {
                         var tableKeys = Layout.Env.GetStringTableKeys(name);

                         if (tableKeys != null)
                         {
                             propertyLayout.Keys.AddRange(tableKeys);
                         }
                         else
                         {
                             if (PvfEnv.IsDevelopmentMode)
                             {
                                 throw new NotSupportedException($"字段值未找到:<{x.Name}>{name}");
                             }
                         }
                     }

                     string closedName = "";
                     if (closedFieldAttr != null)
                     {
                         if (closedFieldAttr.ClosedFieldKey != -1)
                         {
                             propertyLayout.ClosedKeys.Add(closedFieldAttr.ClosedFieldKey);
                         }
                         else
                         {
                             if (!string.IsNullOrEmpty(closedFieldAttr.ClosedFieldName))
                             {
                                 closedName = closedFieldAttr.ClosedFieldName;
                             }
                             else
                             {
                                 closedName = PvfStatics.NameCast(x.Name);
                             }
                         }
                     }

                     if (!string.IsNullOrEmpty(closedName))
                     {
                         closedName = $"[/{closedName}]";

                         var tableKeys = Layout.Env.GetStringTableKeys(closedName);

                         if (tableKeys != null)
                         {
                             propertyLayout.ClosedKeys.AddRange(tableKeys);

                         }
                     }
                 }

                 IPvfConverter converter = null;
                 var cConverterAttr = x.GetCustomAttribute<PvfCustomConverterAttribute>();

                 if (cConverterAttr != null && cConverterAttr.InType != null)
                 {
                     converter = Activator.CreateInstance(cConverterAttr.InType) as IPvfConverter;
                     if (converter == null)
                     {
                         throw new NotSupportedException($"不支持的转换器:{x.Name}({x.PropertyType.Name})");
                     }
                     converter.Init(propertyLayout);
                 }
                 else
                 {
                     var eConverterAttr = x.GetCustomAttribute<PvfDynamicElementAttribute>();
                     if (eConverterAttr != null)
                     {
                         if (x.GetAccessors()[0].IsVirtual)
                         {
                             converter = ConverterStatics.MakeDynamicConverter(Layout.Env, eConverterAttr.EnumType, x.PropertyType, eConverterAttr.FallbackImplType);
                         }
                     }
                 }

                 if (converter == null)
                 {
                     converter = PvfObjectBuilder.Build(x.PropertyType, Layout.Env, propertyLayout);
                 }

                 if (converter == null)
                 {
                     throw new NotSupportedException($"不支持的转换器:{x.Name}({x.PropertyType.Name})");
                 }

                 Type converterWrapperType = null;
                 if (propertyLayout.IsDynamic)
                 {
                     if (converter is IDynamicElementConverter dynamicElementConverter)
                     {
                         converterWrapperType = typeof(PvfObjectConverterDispatchWrapper<,,>).MakeGenericType(typeof(TObj), x.PropertyType, dynamicElementConverter.GetBaseType());
                     }
                     else
                     {
                         throw new NotSupportedException($"属性{x.Name}拥有[PvfDynamicElement]特性,却没有继承IDynamicElementConverter");
                     }
                 }

                 if (converterWrapperType == null)
                 {
                     converterWrapperType = typeof(PvfObjectConverterWrapper<,>).MakeGenericType(typeof(TObj), x.PropertyType);
                 }

                 propertyLayout.ConverterWrapper = Activator.CreateInstance(converterWrapperType) as PvfObjectConverterWrapper;
                 if (propertyLayout.ConverterWrapper == null) return propertyLayout;
                 propertyLayout.ConverterWrapper.Converter = converter;
                 propertyLayout.ConverterWrapper.Setter = PvfObjectBuilder.CreateSetValueDelegate(x);
                 propertyLayout.ConverterWrapper.Getter = PvfObjectBuilder.CreateGetValueDelegate(Layout.ObjType, x.Name);

                 return propertyLayout;
             })
             .Where(x => x != null)
             .ToArray();

            _propertyLayouts = new Dictionary<int, WrapperPvfObjectLayout>();

            var index = 0;
            foreach (var propInfo in propLst)
            {
                if (Layout.IsRequired)
                {
                    _propertyLayouts.Add(index++, propInfo);
                }
                else
                {
                    foreach (var key in propInfo.Keys)
                    {
                        _propertyLayouts.Add(key, propInfo);
                    }
                }
            }
        }

        public override TObj Deserialize(BinaryReader reader, IPvfModule module = null)
        {
            if (reader.PeekChar() == -1)
            {
                return default;
            }

            var obj = Activator.CreateInstance<TObj>();

            //如果isRequired 那么每一个字段都是必须的 
            if (Layout.IsRequired)
            {
                foreach (var layout in _propertyLayouts)
                {
                    if (layout.Value.ConverterWrapper is PvfObjectConverterWrapper<TObj> wrapper)
                    {
                        if (!wrapper.Converter.CanRead(reader))
                        {
                            break;
                        }
                        wrapper.SetValue(reader, obj, module);
                    }
                }
            }
            else
            {
                while (reader.BaseStream.Position != reader.BaseStream.Length)
                {
                    var pk = reader.ReadByte();
                    switch (pk)
                    {
                        case 5:
                            {
                                var key = reader.ReadInt32();

                                if (!reader.UnitNextPack())
                                {
                                    break;
                                }

                                var found = _propertyLayouts.TryGetValue(key, out var foundLayout);
                                if (found)
                                {
                                    if (foundLayout.ConverterWrapper is PvfObjectConverterWrapper<TObj> wrapper)
                                    {
                                        wrapper.SetValue(reader, obj, module);
                                    }
                                    else if (foundLayout.ConverterWrapper is PvfObjectConverterDispatchWrapper<TObj> dispatchWrapper)
                                    {
                                        dispatchWrapper.SetValue(reader, obj, module, ref key);
                                    }
                                    else
                                    {
                                        throw new NotSupportedException("不支持的ConverterWrapper");
                                    }
                                }
                                else
                                {
                                    if (FieldCatcher.CanPush())
                                    {
                                        FieldCatcher.Push(key, Layout.Env.StringTable[key]);
                                    }
                                }
                            }
                            break;
                        default:
                            {
                                if (reader.BaseStream.Position + 4 > reader.BaseStream.Length)
                                {
                                    break;
                                }
                                else
                                {
                                    reader.BaseStream.Position += 4;
                                }
                            }
                            break;
                    }
                }
            }

            return obj;
        }
    }
}
